﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using AutoMapper;
using Jinjia.Approve.Common.Enum;
using Jinjia.Approve.Entity.Entity.JJFlow;
using Jinjia.Approve.IRepository.JJFlow;
using Jinjia.Approve.IService;
using Jinjia.Approve.IService.JJFlow;
using Jinjia.Approve.Model.Input.JJFlow;
using Jinjia.Approve.Model.VM.JJFlow;
using JinJia.Core.Base.Exceptions;
using SqlSugar.Extensions;

namespace Jinjia.Approve.Service.JJFlow
{
    public class JJFlowNodeService : IJJFlowNodeService
    {
        private readonly IFlowNodeRepository _flowNodeRepository;
        private readonly IFlowInstanceRepository _flowInstanceRepository;
        private readonly IFlowInstanceFormRepository _flowInstanceFormRepository;
        private readonly IFlowNodeReferenceRepository _flowNodeReferenceRepository;
        private readonly IJJFlowStepService _jjFlowStepService;
        private readonly IJJFlowTemplateRemoteInterfaceService _jJFlowTemplateRemoteInterfaceService;
        private readonly IMapper _mapper;

        public JJFlowNodeService(
            IFlowNodeRepository flowNodeRepository,
            IFlowInstanceRepository flowInstanceRepository,
            IFlowInstanceFormRepository flowInstanceFormRepository,
            IMapper mapper,
            IFlowNodeReferenceRepository flowNodeReferenceRepository,
            IJJFlowStepService jjFlowStepService,
            IJJFlowTemplateRemoteInterfaceService jJFlowTemplateRemoteInterfaceService)
        {
            _flowNodeRepository = flowNodeRepository;
            _flowInstanceRepository = flowInstanceRepository;
            _flowInstanceFormRepository = flowInstanceFormRepository;
            _mapper = mapper;
            _flowNodeReferenceRepository = flowNodeReferenceRepository;
            _jjFlowStepService = jjFlowStepService;
            _jJFlowTemplateRemoteInterfaceService = jJFlowTemplateRemoteInterfaceService;
        }

        public async Task<List<Node>> GetNodes(int template_id)
        {
            var jjNodes = await _flowNodeRepository.GetNodes(template_id);
            var jjNodeVms = _mapper.Map<List<Node>>(jjNodes);
            return jjNodeVms;
        }

        public async Task<Node> GetPrevNode(int instance_id, int node_id)
        {
            var all_planned_nodes = await GetNodesPlan(instance_id);
            var current_node = all_planned_nodes.FirstOrDefault(t => t.id == node_id);
            var prev_node = all_planned_nodes[all_planned_nodes.IndexOf(current_node) - 1];
            return prev_node;
        }

        public async Task<Node> GetStartNode(int template_id)
        {
            var jjNodes = await _flowNodeRepository.GetStartNode(template_id);
            var jjNodeVms = _mapper.Map<Node>(jjNodes);
            return jjNodeVms;
        }

        public async Task<Node> GetNextNode(int instance_id, int node_id)
        {
            var all_planned_nodes = await GetNodesPlan(instance_id);
            var current_node = all_planned_nodes.FirstOrDefault(t => t.id == node_id);
            if (all_planned_nodes.IndexOf(current_node) == all_planned_nodes.Count() - 1)
                throw new BusinessException("Cannot approve anymore, this is the last item");
            var next_node = all_planned_nodes[all_planned_nodes.IndexOf(current_node) + 1];
            return next_node;
        }

        public async Task<List<Node>> GetNodesPlan(int flow_instance_id)
        {
            var jjNodes = await _flowNodeRepository.GetNodesPlan(flow_instance_id);
            var nodes = _mapper.Map<List<Node>>(jjNodes);
            return nodes;
        }

        public async Task<Node> GetNodeById(int node_id)
        {
            var jjNode = await _flowNodeRepository.GetById(node_id);
            var node = _mapper.Map<Node>(jjNode);
            return node;
        }

        public async Task<List<Node>> GetNodeByIds(List<int> nodeIds)
        {
            if (nodeIds == null || nodeIds.Count == 0)
            {
                return default;
            }
            var jjNode = await _flowNodeRepository.GetByIds(nodeIds);
            return _mapper.Map<List<Node>>(jjNode);
        }

        public async Task<List<Node>> DecidePath(string biz_order_id, Node start_node, string user_id, int instance_id)
        {
            //根据条件决定路径
            //获取node plan
            var plan_nodes = new List<Node> { start_node }; //先加入当前节点
            plan_nodes.AddRange(await CalculateNodesPlanFromStartNode(instance_id, biz_order_id, user_id)); //在加入未来节点
            //保存Nodes到NodeReferences
            var sequence = 0;
            foreach (var node in plan_nodes.Where(a => a.node_type != (int)EnumDb.JJFlowNodeTypeEnum.CondNode))
            {
                await _flowNodeReferenceRepository.Save(new jj_node_reference()
                {
                    flow_instance_id = instance_id,
                    sequence = sequence++,
                    node_id = node.id,
                });
            }

            return plan_nodes;
        }

        public async Task<List<Node>> GetNodeByInstanceIds(List<int> instanceIds)
        {
            return await _flowNodeRepository.GetNodeByInstanceIds(instanceIds);
        }

        /// <summary>
        /// 根据当前节点=起始节点，推算其他节点
        /// </summary>
        /// <param name="flow_instance_id"></param>
        /// <param name="biz_order_id"></param>
        /// <param name="user_id"></param>
        /// <returns></returns>
        private async Task<List<Node>> CalculateNodesPlanFromStartNode(int flow_instance_id, string biz_order_id, string user_id)
        {
            //根据当前节点=起始节点，推算其他节点
            var curr_instance = await _flowInstanceRepository.GetById(flow_instance_id);
            var curr_node = await _flowNodeRepository.GetById(curr_instance.current_node_id);
            var depart_id = await _flowInstanceFormRepository.GetValueByKey(flow_instance_id, "dingding_depart_id");
            //递归查找所有的节点;
            return await GetAllSuccessors(biz_order_id, user_id, depart_id, flow_instance_id, curr_node.id);
        }

        /// <summary>
        /// 获取下游节点列表
        /// </summary>
        /// <param name="biz_order_id"></param>
        /// <param name="user_id"></param>
        /// <param name="depart_id"></param>
        /// <param name="instance_id"></param>
        /// <param name="node_id"></param>
        /// <returns></returns>
        public async Task<List<Node>> GetAllSuccessors(string biz_order_id, string user_id, string depart_id, int instance_id, int node_id)
        {
            var successor = new List<Node>();
            var next_node = await DecideNextNodeByCondition(biz_order_id, user_id, depart_id, instance_id, node_id); //获取下个节点
            successor.Add(next_node);
            if (next_node.node_type != (int)EnumDb.JJFlowNodeTypeEnum.EndNode) //如果不是终点
            {
                if (next_node.node_type == (int)EnumDb.JJFlowNodeTypeEnum.CondNode)
                {
                    //条件节点
                    var path_list = await _jjFlowStepService.GetNextPossiblePath(node_id);
                    //判断下个节点是否为条件节点(本处理只是为了保证程序健壮性，正常情况不会进入异常)
                    var new_next_node = await _flowNodeRepository.GetById(path_list.First().current_node);
                    if (new_next_node == null || new_next_node.node_type != (int)EnumDb.JJFlowNodeTypeEnum.CondNode)
                        throw new BusinessException("不是Cond Node却有多条路径");

                    //如果有多条路径则判断哪个路径条件满足
                    jj_flow_step chosen_path = null;
                    foreach (var path in path_list)
                    {
                        if (path.condition_type == (int)EnumDb.JJFlowSequenceConditionTypeEnum.Expression)
                        {
                            //解析表达式
                            if (await CheckConditionNew(instance_id, path.condition_name))
                            {
                                chosen_path = path;
                                break;
                            }
                        }
                        else if (path.condition_type == (int)EnumDb.JJFlowSequenceConditionTypeEnum.Function)
                        {
                            if (await _jjFlowStepService.ConditionSatisfied(biz_order_id, path.id, depart_id, user_id))
                            {
                                chosen_path = path;
                                break;
                            }
                        }
                        else if (path.condition_type == (int)EnumDb.JJFlowSequenceConditionTypeEnum.RemoteFunction)
                        {
                            if (await _jJFlowTemplateRemoteInterfaceService.GetRemoteInterfaceAsync<jj_flow_step, bool>(new RemoteInterfaceInput<jj_flow_step>
                            {
                                biz_order_id = biz_order_id,
                                depart_id = depart_id,
                                start_user = user_id.ObjToInt(),
                                FunctionCode = path.condition_name,
                                Content = path,
                            }))
                            {
                                chosen_path = path;
                                break;
                            }
                        }
                    }

                    //返回满足条件的跳转路径的目标节点
                    var jjNode = await _flowNodeRepository.GetById(chosen_path.current_node);
                    successor.AddRange(await GetAllSuccessors(biz_order_id, user_id, depart_id, instance_id, jjNode.id)); //递归获取下游节点列表
                }
                else
                {
                    successor.AddRange(await GetAllSuccessors(biz_order_id, user_id, depart_id, instance_id, next_node.id)); //递归获取下游节点列表
                }
            }

            return successor;
        }

        private async Task<Node> DecideNextNodeByCondition(string biz_order_id, string user_id, string depart_id, int instance_id, int node_id)
        {
            var path_list = await _jjFlowStepService.GetNextPossiblePath(node_id);
            if (path_list.Count == 1) //只有一个路径
            {
                var node = await _flowNodeRepository.GetById(path_list.First().current_node);
                var jjNodeVm = _mapper.Map<Node>(node);
                return jjNodeVm;
            }

            //判断下个节点是否为条件节点(本处理只是为了保证程序健壮性，正常情况不会进入异常)
            var next_node = await _flowNodeRepository.GetById(path_list.First(x => x.condition_type != 0).current_node);
            if (next_node == null || next_node.node_type != (int)EnumDb.JJFlowNodeTypeEnum.CondNode)
                throw new BusinessException("不是Cond Node却有多条路径");

            //如果有多条路径则判断哪个路径条件满足
            jj_flow_step chosen_path = null;
            foreach (var path in path_list)
            {
                if (path.condition_type == (int)EnumDb.JJFlowSequenceConditionTypeEnum.Expression)
                {
                    //解析表达式
                    if (await CheckConditionNew(instance_id, path.condition_name))
                    {
                        chosen_path = path;
                        break;
                    }
                }
                else if (path.condition_type == (int)EnumDb.JJFlowSequenceConditionTypeEnum.Function)
                {
                    if (await _jjFlowStepService.ConditionSatisfied(biz_order_id, path.id, depart_id, user_id))
                    {
                        chosen_path = path;
                        break;
                    }
                }
                else if (path.condition_type == (int)EnumDb.JJFlowSequenceConditionTypeEnum.RemoteFunction)
                {
                    if (await _jJFlowTemplateRemoteInterfaceService.GetRemoteInterfaceAsync<jj_flow_step, bool>(new RemoteInterfaceInput<jj_flow_step>
                    {
                        biz_order_id = biz_order_id,
                        depart_id = depart_id,
                        start_user = user_id.ObjToInt(),
                        FunctionCode = path.condition_name,
                        Content = path
                    }))
                    {
                        chosen_path = path;
                        break;
                    }
                }
            }

            //返回满足条件的跳转路径的目标节点
            var jjNode = await _flowNodeRepository.GetById(chosen_path.to_node);
            var nodeVm = _mapper.Map<Node>(jjNode);
            return nodeVm;
        }

        /// <summary>
        /// 解析条件表达式返回结果new
        /// </summary>
        /// <param name="instance_id"></param>
        /// <param name="ori_condition_str"></param>
        /// <returns></returns>
        public async Task<bool> CheckConditionNew(int instance_id, string ori_condition_str)
        {
            var result = false;
            if (string.IsNullOrWhiteSpace(ori_condition_str)) return result;
            try
            {
                var new_expression = ori_condition_str;
                //首先分解符号
                var variables = ori_condition_str.Split(new char[13] { '+', '-', '*', '/', '%', '(', ')', '>', '<', '=', '&', '^', '.' });
                //对于每一个变量variable,获取计算出结果
                foreach (var variable in variables)
                {
                    if (variable.Contains("@"))
                    {
                        var response = await GetValueFromFlowInstance(instance_id, variable);
                        //查找到，则将变量的值替换掉
                        var reg = new Regex(variable);
                        new_expression = reg.Replace(new_expression, response, 1);
                    }
                }

                new_expression = SpecialTreatment(new_expression);
                new_expression = new_expression.Replace("&", " AND ").Replace("^", " OR ");
                result = (bool)new DataTable().Compute(new_expression, "");
            }
            catch (Exception ex)
            {
                //Console.WriteLine("计算失败" + ex.Message);
                throw ex;
            }

            return result;
        }

        /// <summary>
        /// 根据id和key获取对应的值
        /// </summary>
        /// <param name="instance_id"></param>
        /// <param name="form_key"></param>
        /// <returns></returns>
        private async Task<string> GetValueFromFlowInstance(int instance_id, string form_key)
        {
            form_key = form_key.Replace("@", "").Trim();
            var result = string.Empty;
            var form_info = await _flowInstanceFormRepository.GetModelByKey(instance_id, form_key);
            if (form_info != null)
            {
                result = form_info.form_value;
            }

            return result;
        }

        /// <summary>
        /// like,notlike,in,notin 特殊处理
        /// </summary>
        /// <param name="new_expression"></param>
        /// <returns></returns>
        private string SpecialTreatment(string new_expression)
        {
            if (new_expression.Contains(".like.") || new_expression.Contains(".notlike.") || new_expression.Contains(".in.") || new_expression.Contains(".notin."))
            {
                string[] like_str = new_expression.Split(new char[4] { '(', ')', '&', '^' });
                foreach (var item in like_str)
                {
                    var like_result = "false";
                    var param1 = string.Empty;
                    var param2 = string.Empty;
                    if (item.Contains(".like."))
                    {
                        param1 = item.Substring(0, item.IndexOf(".like")).Trim();
                        param2 = item.Substring(item.IndexOf("like.") + 5).Trim();
                        if (param1.Contains(param2))
                        {
                            like_result = "true";
                        }

                        new_expression = new_expression.Replace(item, like_result);
                    }
                    else if (item.Contains(".notlike."))
                    {
                        param1 = item.Substring(0, item.IndexOf(".notlike")).Trim();
                        param2 = item.Substring(item.IndexOf("notlike.") + 8).Trim();
                        if (!param1.Contains(param2))
                        {
                            like_result = "true";
                        }

                        new_expression = new_expression.Replace(item, like_result);
                    }
                    else if (item.Contains(".in."))
                    {
                        param1 = item.Substring(0, item.IndexOf(".in")).Trim();
                        param2 = item.Substring(item.IndexOf("in.") + 3).Trim();
                        var new_arry = param2.Trim().Split(',');
                        if (new_arry.Contains(param1))
                        {
                            like_result = "true";
                        }

                        new_expression = new_expression.Replace(item, like_result);
                    }
                    else if (item.Contains(".notin."))
                    {
                        param1 = item.Substring(0, item.IndexOf(".notin")).Trim();
                        param2 = item.Substring(item.IndexOf("notin.") + 6).Trim();
                        var new_arry = param2.Trim().Split(',');
                        if (!new_arry.Contains(param1))
                        {
                            like_result = "true";
                        }

                        new_expression = new_expression.Replace(item, like_result);
                    }
                }
            }

            return new_expression;
        }
    }
}